// file: arch/x86/include/arch/apic.h
// autor: jiangxinpeng
// time: 2022.1.22
// copyright: (C) 2020-2050 by jiangxinpeng,All right are reserved.

#ifndef ARCH_APIC_H
#define ARCH_APIC_H

#include <lib/type.h>

#define APIC_ENINTERRUPT 0
#define APIC_DISINTERRUPT 1

#define APIC_LVT_VECTOR 0x64                   // lvt vector start
#define LOCAL_TIMER_VECTOR APIC_LVT_VECTOR + 0 // timer vector number
#define LOCAL_ERROR_VECTOR APIC_LVT_VECTOR + 1 // APIC error vector number

#define IA32_APIC_BASE_MSR 0x1B            // APIC base MSR
#define IA32_APIC_BASE_MSR_BSP 0x100       // processor is BSP
#define IA32_APIC_BASE_MSR_ENABLE 0x800    // enable APIC
#define IA32_APIC_BASE_MSR_BASE 0xFFFFF000 // APIC base mask
#define IA32_APIC_BASE_MSR_BASE_SHIFT 12

// APIC LAPIC register
#define APIC_LAPIC_ID_REG 0x20 // CPU ID
#define APIC_LAPIC_ID_SHIFT 24
#define APIC_LAPIC_ID_MASK 0xFF000000 // CPU ID mask

#define APIC_LAPIC_VERSION_REG 0x30      // version
#define APIC_LAPIC_VERSION_MASK 0xF      // version number
#define APIC_LAPIC_MAX_LVT_ENTRY 0xF0000 // max LVT entry

// APIC ICR register
#define APIC_ICR_HIGH_REG 0x310 // interrupt command register
#define APIC_ICR_LOW_REG 0x300  // interrupt command register

// APIC EOF register
#define APIC_EOF_REG 0xB0
// APIC IRR register
#define APIC_IRR_REG 0x200
// APIC ISR register
#define APIC_ISR_REG 0x100

// APIC Timer register
// divide configuration register
#define APIC_DIVIDE_REG 0x3E0
#define APIC_DIVIDE_VAL_2 0x0
#define APIC_DIVIDE_VAL_4 0x1
#define APIC_DIVIDE_VAL_8 0x2
#define APIC_DIVIDE_VAL_16 0x3
#define APIC_DIVIDE_VAL_32 0x8
#define APIC_DIVIDE_VAL_64 0x9
#define APIC_DIVIDE_VAL_128 0xA
#define APIC_DIVIDE_VAL_1 0xB
// initialize count register
#define APIC_TIMER_INIT_COUNT_REG 0x380
// Current count register
#define APIC_TIMER_CURRENT_COUNT_REG 0x390

// APIC Spurious vector register
#define APIC_SPURIOUS_VECTOR_REG 0xF0
#define APIC_SPURIOUS_VECTOR_VEC_MASK 0xFF
#define APIC_SPURIOUS_VECTOR_SOFT_ENABLE 0x100
#define APIC_SPURIOUS_VECTOR_SOFT_DISABLE 0x000
#define APIC_SPURIOUS_VECTOR_SUPBROADCASR_EOF 0x1000

// APIC LVT register
#define APIC_LVT_CMCI_REG 0x2f0                // send corrected machine check error interrupt
#define APIC_LVT_TIMER_REG 0x320               // send APIC timer interrupt
#define APIC_LVT_THERMAL_MONITOR_REG 0x330     // send thermal monitor interrupt
#define APIC_LVT_PERFORMANCE_MONITOR_REG 0x340 // send performance counter interrupt
#define APIC_LVT_LINT0_REG 0x350               // forward the interrupt from the LINT0 pins
#define APIC_LVT_LINT1_REG 0x360               // forward the interrupt from the LINT1 pins
#define APIC_LVT_ERROR_REG 0x370               // send APIC error interrupt occur in internal

// logical destination mode register
#define APIC_LOGICAL_DEST_REG 0xD0
#define APIC_LOGICAL_DEST_APICID_MASK 0xFF000000 // APIC ID mask
#define APIC_LOGICAL_DEST_APICID_SHIFT 24

// destination format register
#define APIC_DEST_FORMAT_REG 0xE0
#define APIC_DEST_FORMAT_MODEL_SHIFT 28
#define APIC_DEST_FORMAT_MODEL_CLUSTER 0x0 // cluster mode
#define APIC_DEST_FORMAT_MODEL_FLAT 0xF    // flat mode

// APIC ICR register field
#define APIC_ICR_DEST_SHIFT 56                // ICR dest
#define APIC_ICR_DEST_SHORHAND_SHIFT 18       // ICR dest shift
#define APIC_ICR_DEST_SHORTHAND_NONE 0x0      // dest cpu specific by destination
#define APIC_ICR_DEST_SHORTHAND_SELF 0x1      // dest cpu is self
#define APIC_ICR_DEST_SHORTHAND_ALLINC 0x2    // send broadcast IPI to all CPU
#define APIC_ICR_DEST_SHORTHAND_ALLEXC 0x3    // send broadcast IPI to all CPU(excluding itself)
#define APIC_ICR_DELIVERY_MODE_SHIFT 8        // delivery mode
#define APIC_ICR_DELIVERY_MODE_FIX 0x0        // send vector to targe CPUs
#define APIC_ICR_DELIVERY_MODE_LOW_PRIOR 0x01 // send vector to targe CPUs which lowest priority
#define APIC_ICR_DELIVERY_MODE_SMI 0x02       // send a SMI to targe CPUs
#define APIC_ICR_DELIVERY_MODE_NMI 0x04       // send a NMI to targe CPUs
#define APIC_ICR_DELIVERY_MODE_INIT 0x05      // Send Init IPI to targe CPUs
#define APIC_ICR_DELIVERY_MODE_STARTUP 0x06   // send StartUp IPI to targe CPUs
#define APIC_ICR_DEST_MODE_SHIFT 11           // dest mode
#define APIC_ICR_DEST_MODE_PHY 0              // physical
#define APIC_ICR_DEST_MODE_LOG 1              // logical
#define APIC_ICR_DELIVERY_STATUS_MASK 0x1000  // delivery staus mask
#define APIC_ICR_DELIVERY_STATUS_IDLE 0       // idle
#define APIC_ICR_DELIVERY_STATUS_SENDPEND 1   // send pending
#define APIC_ICR_LEVEL_SHIFT 14               // level
#define APIC_ICR_LEVEL_DEASSERT 0             // deassert(delivery mode 101b specific to INIT Level De-assert)
#define APIC_ICR_LEVEL_ASSERT 1               // assert(delivery mode 101b specific to INIT)
#define APIC_ICR_TRIGMODE_SHIFT 15            // trigger mode
#define APIC_ICR_TRIGMODE_EDGE 0              // edge trigger
#define APIC_ICR_TRIGMODE_LEVEL 1             // level trigger

// APIC Error status register
#define APIC_ERR_STATUS_REG 0x280
#define APIC_ERR_STATUS_SEND_CHKSUM 0x1       // send checksum error(only pentinum and P6)
#define APIC_ERR_STATUS_RECV_CHKSUM 0x2       // receive checksum error(only pentinum and P6)
#define APIC_ERR_STATUS_SEND_ACCEPT 0x4       // send accept error(only pentinum and P6)
#define APIC_ERR_STATUS_RECV_ACEEPT 0x8       // send accept error(only pentinum and P6)
#define APIC_ERR_STATUS_REDIRCT_IPI 0x10      // try to send a Lowest Priority Mode IPI,but not support by CPU
#define APIC_ERR_STATUS_SEND_ILLEGAL_VEC 0x20 // try to a invalid vector between 0 to 15
#define APIC_ERR_STATUS_RECV_ILLEGAL_VEC 0x40 // receive a invalid vector between 0 to 15
#define APIC_ERR_STATUS_ILLEGAL_REGADDR 0x80  // try to access a register which no present

// APIC LVT filed
// timer mode
#define APIC_LVT_TIMER_MODE_SHIFT 17
#define APIC_LVT_TIMER_MODE_ONE_SHOT 0x0     // dec count to 0,active interrupt and stop count
#define APIC_LVT_TIMER_MODE_PERIODIC 0x1     // repeat dec count to 0,active interrupt and continue
#define APIC_LVT_TIMER_MODE_TSC_DEADLINE 0x2 // while reachable to TSC just active interrupt
// mask
#define APIC_LVT_MASK_SHIFT 16
#define APIC_LVT_MASK_NOTMASKED 0
#define APIC_LVT_MASK_MASKED 1
// interrupt pin polarity
#define APIC_LVT_PIN_SHIFT 13
#define APIC_LVT_PIN_HIGH 0 // LINT0/LINT1 pin is active high
#define APIC_LVT_PIN_LOW 1  // LINT0/LINT1 pin is active low
// delivery mode
#define APIC_LVT_DELIVERY_MODE_SHIFT 8
#define APIC_LVT_DELIVERY_MODE_FIX 0x0    // send vector to cpu
#define APIC_LVT_DELIVERY_MODE_SMI 0x2    // send SMI to cpu
#define APIC_LVT_DELIVERY_MODE_NMI 0x4    // send NMI to cpu
#define APIC_LVT_DELIVERY_MODE_EXTINT 0x7 // response interrupt through likely 8259A PIC
// trig mode
#define APIC_LVT_TRGMODE_SHIFT 15
#define APIC_LVT_TRGMODE_EDGE 0x0
#define APIC_LVT_TRGMODE_LEVEL 0x1

// APIC IPI function
#define SendIPIToCPU(cpu, vendor, delivery_mode, dest_mode, trig, level) APICSendIPI(vendor, delivery_mode, cpu, dest_mode, APIC_ICR_DEST_SHORTHAND_NONE, trig, level)
#define SendIPIToAllCPU(vendor, delivery_mode, dest_mode, trig, level) APICSendIPI(vendor, delivery_mode, 0, dest_mode, APIC_ICR_DEST_SHORTHAND_ALLINC, trig, level)
#define SendIPIToEXCAllCPU(vendor, delivery_mode, dest_mode, trig, level) APICSendIPI(vendor, delivery_mode, 0, dest_mode, APIC_ICR_DEST_SHORTHAND_ALLEXC, trig, level)

#define SendStartupIPI(cpu, vendor) SendIPIToCPU(cpu, vendor, APIC_ICR_DELIVERY_MODE_STARTUP, APIC_ICR_DEST_MODE_PHY, 0, 1)
#define SendNMI(cpu) SendIPIToCPU(cpu, 0, APIC_ICR_DELIVERY_MODE_NMI, APIC_ICR_DEST_MODE_PHY, 0, 1)
#define SendSMI(cpu) SendIPIToCPU(cpu, 0, APIC_ICR_DELIVERY_MODE_SMI, APIC_ICR_DEST_MODE_PHY, 0, 1)
#define SendINIT(cpu) SendIPIToCPU(cpu, 0, APIC_ICR_DELIVERY_MODE_INIT, APIC_ICR_DEST_MODE_PHY, 0, 1)
#define SendVendor(cpu, vendor) SendIPIToCPU(cpu, vendor, ICR_DELIVERY_MODE_VENDOR, APIC_ICR_DEST_MODE_PHY, 0, 1)

typedef enum
{
    APIC_ID = 0,
    APIC_VER,
    APIC_DESTMODE,
    APIC_LOGICAL_DEST,
    APIC_ICR_LOW,
    APIC_ICR_HIGH,
    APIC_EOF,
    APIC_ERR,
    APIC_INIT_COUNT,
    APIC_CUR_COUNT,
    APIC_DIVIDE_CFG,
} apic_reg_t;

typedef enum
{
    APIC_LVT_CMCI,
    APIC_LVT_TIMER,
    APIC_LVT_THERMAL,
    APIC_LVT_PERFORMANCE,
    APIC_LVT_LINT0,
    APIC_LVT_LINT1,
    APIC_LVT_ERR,
} apic_lvt_t;

extern uint8_t apic_init;

// system smp timer
#define SMPTimerHardwareInit() APICTimerInit(100000)

// apic interrupt handler
void APICTimerUpdate();
void APICPrintError();

//apic software interface
int CheckAPICSupport();
address_t GetAPICMSRBase();
void SetAPICMSRBase();
void EnableAPIC();
void DebugAPIC();
void APICInit();
void APICSetModel(uint32_t model);
void APICSendIPI(uint64_t vendor, uint64_t delivery_mode, uint64_t dest, uint64_t dest_mode, uint64_t dest_short, uint64_t trigger, uint64_t level);
int APICOnISR(uint8_t vec);
int APICOnIRR(uint8_t vec);
void SendEOF();
void APICSetLVT(apic_lvt_t lvt_type, uint32_t vector, uint32_t delivey_mode, uint32_t pin, uint32_t trig, uint32_t mask, uint32_t timer_mode);
uint8_t APICGetID();
uint8_t APICGetVer();

void APICTimerInit(uint32_t ticks);
void APICMapVbase();
#endif